import json

from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status

from apps.roles.serializers import RoleSerializer
from apps.tenants.models import Tenant
from apps.menus.models import Menu
from apps.roles.models import Role

User = get_user_model()


class RoleAPITestCase(APITestCase):
    def setUp(self):
        # 创建租户
        self.tenant = Tenant.objects.create(name="Test Tenant", code='test')
        self.other_tenant = Tenant.objects.create(name="Other Tenant", code='other')

        # 创建管理员和普通用户
        self.admin = User.objects.create_superuser(
            username='admin',
            password='admin123',
            email='admin@test.com',
            tenant=self.tenant
        )
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123',
            full_name='Test User',
            role='teacher',
            email='test@test.com',
            tenant=self.tenant
        )
        self.other_user = User.objects.create_user(
            username='otheruser',
            password='testpass123',
            full_name='Other User',
            role='student',
            email='other@test.com',
            tenant=self.other_tenant
        )

        # 登录 admin
        self.client.force_authenticate(user=self.admin)

        # 创建测试菜单
        self.menu1 = Menu.objects.create(
            title="Dashboard",
            name="Dashboard",
            path="/dashboard",
            component="Dashboard.vue",
            icon="ep:home",
            menu_type="menu",
            order=0,
            is_active=True,
            tenant=self.tenant
        )
        self.menu2 = Menu.objects.create(
            title="Users",
            name="UserManagement",
            path="/users",
            component="UserList.vue",
            icon="ep:user",
            menu_type="menu",
            parent=self.menu1,
            order=1,
            is_active=True,
            tenant=self.tenant
        )
        self.button = Menu.objects.create(
            title="Add User",
            name="UserCreate",
            menu_type="button",
            parent=self.menu2,
            order=0,
            is_active=True,
            tenant=self.tenant
        )

        # 创建测试角色
        self.role = Role.objects.create(
            name="Teacher Role",
            code="teacher_role",
            description="Default role for teachers",
            order=1,
            is_active=True,
            tenant=self.tenant
        )
        self.role.menus.set([self.menu1])  # 授予 Dashboard 权限

    def test_create_role(self):
        """测试创建角色"""
        url = reverse('role-list')
        data = {
            'name': 'Admin Role',
            'code': 'admin_role',
            'description': 'Full access role',
            'menus': [self.menu1.id, self.menu2.id],
            'order': 0,
            'is_active': True
        }
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Role.objects.count(), 2)

        role = Role.objects.get(code='admin_role')
        self.assertEqual(role.name, 'Admin Role')
        self.assertEqual(role.tenant, self.tenant)
        self.assertEqual(role.menus.count(), 2)
        self.assertIn(self.menu1, role.menus.all())
        self.assertIn(self.menu2, role.menus.all())

    def test_update_role(self):
        """测试更新角色"""
        url = reverse('role-detail', args=[self.role.id])
        data = {
            'name': 'Updated Teacher Role',
            'code': 'teacher_role',
            'menus': [self.menu2.id],
            'is_active': False
        }
        response = self.client.put(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.role.refresh_from_db()
        self.assertEqual(self.role.name, 'Updated Teacher Role')
        self.assertFalse(self.role.is_active)
        self.assertEqual(self.role.menus.count(), 1)
        self.assertIn(self.menu2, self.role.menus.all())
        self.assertNotIn(self.menu1, self.role.menus.all())

    def test_delete_role(self):
        """测试删除角色"""
        url = reverse('role-detail', args=[self.role.id])
        response = self.client.delete(url)
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(Role.objects.filter(id=self.role.id).exists())

    def test_list_roles(self):
        """测试获取角色列表"""
        url = reverse('role-list')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        data = response.data['results']
        self.assertEqual(len(data), 1)
        self.assertEqual(data[0]['name'], 'Teacher Role')
        self.assertEqual(data[0]['code'], 'teacher_role')
        self.assertEqual(data[0]['menus'], [self.menu1.id])

    def test_retrieve_role_with_menu_tree(self):
        """测试获取角色详情，包含 menu_tree 结构"""
        url = reverse('role-detail', args=[self.role.id])
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        data = response.data

        # 检查 menu_tree 字段
        menu_tree = data['menu_tree']
        self.assertIsInstance(menu_tree, list)
        self.assertGreaterEqual(len(menu_tree), 1)

        # 找到 Dashboard 节点
        dashboard_node = next((item for item in menu_tree if item['name'] == 'Dashboard'), None)
        self.assertIsNotNone(dashboard_node)
        self.assertTrue(dashboard_node['checked'])  # 因为 role 有 menu1 权限

        # 检查 Users 子菜单
        users_node = next((child for child in dashboard_node['children'] if child['name'] == 'UserManagement'), None)
        self.assertIsNotNone(users_node)
        self.assertFalse(users_node['checked'])  # 未授权

        # 检查按钮（不应出现在 menu_tree，因为是 menu_type=button）
        self.assertFalse(any(
            child['name'] == 'UserCreate'
            for child in users_node['children']
        ))

    def test_menu_tree_excludes_buttons(self):
        """测试 menu_tree 不包含 button 类型菜单"""
        from apps.roles.serializers import RoleSerializer

        serializer = RoleSerializer(self.role)
        menu_tree = serializer.data['menu_tree']

        def has_button_node(nodes):
            for node in nodes:
                if node['name'] == 'UserCreate':
                    return True
                if has_button_node(node['children']):
                    return True
            return False

        self.assertFalse(has_button_node(menu_tree))

    def test_serializer_menu_tree_structure(self):
        """测试序列化器生成的 menu_tree 结构正确"""
        serializer = RoleSerializer(self.role)
        data = serializer.data

        self.assertIn('menu_tree', data)
        tree = data['menu_tree']
        self.assertEqual(len(tree), 1)  # 只有一个根菜单：Dashboard
        root = tree[0]
        self.assertEqual(root['name'], 'Dashboard')
        self.assertTrue(root['checked'])
        self.assertEqual(len(root['children']), 1)
        child = root['children'][0]
        self.assertEqual(child['name'], 'UserManagement')
        self.assertFalse(child['checked'])

    def test_only_admin_can_modify(self):
        """测试仅管理员可修改角色"""
        self.client.force_authenticate(user=self.user)
        url = reverse('role-list')
        data = {'name': 'Hacked Role', 'code': 'hacked'}
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_tenant_isolation_on_list(self):
        """测试租户数据隔离 - 列表"""
        self.client.force_authenticate(user=self.other_user)
        url = reverse('role-list')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_tenant_isolation_on_create(self):
        """测试租户隔离 - 创建角色"""
        self.client.force_authenticate(user=self.other_user)
        url = reverse('role-list')
        data = {
            'name': 'Other Role',
            'code': 'other_role',
            'menus': []
        }
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_filter_roles_by_status(self):
        """测试按 is_active 过滤角色"""
        inactive_role = Role.objects.create(
            name="Inactive Role",
            code="inactive",
            is_active=False,
            tenant=self.tenant
        )

        url = reverse('role-list') + '?is_active=true'
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        print(response.data)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], 'Teacher Role')

        url = reverse('role-list') + '?is_active=false'
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], 'Inactive Role')

    def test_create_role_with_empty_menus(self):
        """测试创建角色时 menus 为空"""
        url = reverse('role-list')
        data = {
            'name': 'No Menu Role',
            'code': 'no_menu',
            'menus': [],
            'is_active': True
        }
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        role = Role.objects.get(code='no_menu')
        self.assertEqual(role.menus.count(), 0)

    def test_update_role_remove_all_menus(self):
        """测试更新角色时移除所有菜单权限"""
        url = reverse('role-detail', args=[self.role.id])
        data = {
            'name': 'Teacher Role',
            'code': 'teacher_role',
            'menus': []
        }
        response = self.client.put(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.role.refresh_from_db()
        self.assertEqual(self.role.menus.count(), 0)